'''
Created on Apr 12, 2012

@author: dolphinigle

Calculates the SHA-1 of a sequence of bytes (integers between 0 and 256)
'''

import util

def CalculateSHA1(text):
  '''
  Calculates SHA-1 of [0, 1, 0, 1, ...] that kind of array
  '''
  print 'Calculating SHA1...'
  
  # Transform into bits
  bits = []
  for byte in text:
    assert 0 <= byte < 256
    byte_cpy = byte
    for _ in range(8):
      bits.append(byte_cpy % 2)
      byte_cpy /= 2
  
  org_len = len(bits)

  
  # append 1
  bits.append(1);
  
  # append 0s until length % 448 == 0
  while len(bits) % 512 != 448:
    bits.append(0)
  
  # append length of message
  for i in range(63, -1, -1):
    bits.append(org_len / 2**i)
    org_len %= 2**i
  
  assert len(bits) % 512 == 0
  
  # constants
  modu = 2**32
  h0 = 0x67452301
  h1 = 0xEFCDAB89
  h2 = 0x98BADCFE
  h3 = 0x10325476
  h4 = 0xC3D2E1F0
  
  
  for i in range(len(bits) / 512):
    # break message into 512 bits chunks, each into 32-bit big endian words
    print 'Chunk', i, '/', len(bits) / 512
    chunks = []
    for j in range(16):
      val = 0
      for k in range(32):
        val *= 2
        val += bits[i * 512 + j * 32 + k]
      chunks.append(val)
    
    # extend into 80 32-bits words
    for i in range(16, 80):
      chunks.append(util.BitLeftRotate(\
          32, chunks[i-3] ^ chunks[i-8] ^ chunks[i-14] ^ chunks[i-16]))

    # Initialize hash value for this chunk:
    a = h0
    b = h1
    c = h2
    d = h3
    e = h4

    for i in range(80):
      if 0 <= i <= 19:
        f = (b & c) | ((~b) & d)
        k = 0x5A827999
      elif 20 <= i <= 39:
        f = b ^ c ^ d
        k = 0x6ED9EBA1
      elif 40 <= i <= 59:
        f = (b & c) | (b & d) | (c & d)
        k = 0x8F1BBCDC
      elif 60 <= i <= 79:
        f = b ^ c ^ d
        k = 0xCA62C1D6
      
      temp = util.MultiLeftRotate(32, a, 5) + f + e + k + chunks[i]
      temp %= modu
      
      e = d
      d = c
      c = util.MultiLeftRotate(32, b, 30)
      b = a
      a = temp

    # Add this chunk's hash to result so far:
    h0 = (h0 + a) % modu
    h1 = (h1 + b) % modu
    h2 = (h2 + c) % modu
    h3 = (h3 + d) % modu
    h4 = (h4 + e) % modu

  print 'DONE!'
  
  return util.ToHex(32, h0) + util.ToHex(32, h1) +\
         util.ToHex(32, h2) + util.ToHex(32, h3) + util.ToHex(32, h4)

def SHA1Byte(by):
  bits = []
  for i in by:
    k = ord(i)
    for _ in range(8):
      bits.append(k % 2)
      k /= 2
  return CalculateSHA1(bits)
